package top.zhangjianyong.blog.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.zhangjianyong.blog.enums.RespCode;
import top.zhangjianyong.blog.constant.ThreadPoolConstant;
import top.zhangjianyong.blog.entity.dto.BlogTagDTO;
import top.zhangjianyong.blog.entity.dto.TagDTO;
import top.zhangjianyong.blog.entity.po.Blog;
import top.zhangjianyong.blog.entity.po.BlogTag;
import top.zhangjianyong.blog.entity.po.Tag;
import top.zhangjianyong.blog.exception.ApiException;
import top.zhangjianyong.blog.mapper.TagMapper;
import top.zhangjianyong.blog.params.tag.*;
import top.zhangjianyong.blog.response.ApiResponse;
import top.zhangjianyong.blog.service.BlogService;
import top.zhangjianyong.blog.service.BlogTagService;
import top.zhangjianyong.blog.service.TagService;
import top.zhangjianyong.blog.utils.ColorUtil;

/**
 * 标签
 *
 * @author HDN
 * @since 2024/7/30 下午10:48
 */
@Service
public class TagServiceImpl extends ServiceImpl<TagMapper, Tag> implements TagService {

  private final TagService tagService;
  private final BlogTagService blogTagService;
  private final BlogService blogService;

  public TagServiceImpl(
      @Lazy TagService tagService, BlogTagService blogTagService, BlogService blogService) {
    this.tagService = tagService;
    this.blogTagService = blogTagService;
    this.blogService = blogService;
  }

  @Override
  public ApiResponse page(TagPageParam param) {
    MPJLambdaWrapper<Tag> wrapper =
        new MPJLambdaWrapper<>(Tag.class, "t")
            .like(StrUtil.isNotBlank(param.name()), Tag::getName, param.name())
            .orderByDesc(Tag::getId);
    Page<TagDTO> page =
        baseMapper.selectJoinPage(
            new Page<>(param.pageNum(), param.pageSize()), TagDTO.class, wrapper);

    for (TagDTO tagDTO : page.getRecords()) {
      List<Blog> blogs = blogService.listByTag(tagDTO.getId());
      tagDTO.setBlogs(blogs);
    }

    return ApiResponse.success(page);
  }

  @Override
  public ApiResponse save(TagSaveParam param) {
    Tag tag = getTagByName(param.name());
    if (tag != null) {
      return ApiResponse.success(tag);
    }
    tag = new Tag();
    BeanUtils.copyProperties(param, tag);

    // 处理颜色值：格式化为标准格式或生成随机颜色
    tag.setColor(ColorUtil.safeGetColor(tag.getColor()));

    return save(tag) ? ApiResponse.success(tag) : ApiResponse.fail(RespCode.FILE_READ_ERROR);
  }

  @Override
  public ApiResponse list(TagListParam param) {
    List<Tag> list =
        lambdaQuery()
            .like(StrUtil.isNotBlank(param.name()), Tag::getName, param.name())
            .orderByDesc(Tag::getId)
            .list();
    return ApiResponse.success(list);
  }

  @Override
  public ApiResponse remove(TagRemoveParam param) {
    boolean remove = removeById(param.id());
    if (!remove) throw new ApiException(RespCode.TAG_REMOVE_ERROR);
    // 删除博客关联关系
    blogTagService.removeByTag(param.id());

    return ApiResponse.success();
  }

  @Override
  public ApiResponse update(TagUpdateParam param) {
    Tag tag = getById(param.id());
    if (tag == null) {
      throw new ApiException(RespCode.TAG_REMOVE_ERROR);
    }

    BeanUtils.copyProperties(param, tag);

    // 更新颜色
    if (StrUtil.isBlank(tag.getColor())) {
      String newColor = ColorUtil.safeGetColor(param.color());
      tag.setColor(newColor);
    }

    if (StrUtil.isBlank(tag.getIcon())) {
      tag.setIcon("🏷️");
    }
    return updateById(tag) ? ApiResponse.success(tag) : ApiResponse.fail(RespCode.FILE_READ_ERROR);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public List<Tag> saveBatch(List<String> tags) {
    if (CollectionUtil.isEmpty(tags)) return new ArrayList<>();
    // 查询已存在的便签
    List<Tag> list = lambdaQuery().in(Tag::getName, tags).list();
    Set<String> tagNames = list.stream().map(Tag::getName).collect(Collectors.toSet());
    // 筛选需要保存的标签
    List<Tag> saveTags =
        tags.stream()
            .filter(name -> !tagNames.contains(name))
            .map(
                name -> {
                  Tag tag = new Tag();
                  tag.setName(name);
                  // 为新标签自动生成随机颜色，确保格式统一为#FFFFFF
                  tag.setColor(ColorUtil.generateRandomColor());
                  // 为新标签设置默认图标
                  tag.setIcon("🏷️");
                  return tag;
                })
            .toList();
    if (CollectionUtil.isNotEmpty(saveTags)) {
      tagService.saveBatch(saveTags);
      list.addAll(saveTags);
    }
    return list;
  }

  @Override
  public BlogTagDTO listByBlog(Long blogId) {
    MPJLambdaWrapper<Tag> wrapper =
        new MPJLambdaWrapper<Tag>()
            .selectAll(Tag.class)
            .leftJoin(BlogTag.class, BlogTag::getTagId, Tag::getId)
            .eq(BlogTag::getBlogId, blogId);
    List<Tag> tags = baseMapper.selectJoinList(Tag.class, wrapper);
    BlogTagDTO blogTagDTO = new BlogTagDTO();
    blogTagDTO.setTags(tags);
    blogTagDTO.setBlogId(blogId);
    return blogTagDTO;
  }

  @Async(ThreadPoolConstant.ASYNC_SQL_EXECUTOR_BEAN)
  @Override
  public CompletableFuture<BlogTagDTO> listByBlogAsync(Long blogId) {
    return CompletableFuture.completedFuture(listByBlog(blogId));
  }

  @Override
  public ApiResponse listNotEmpty() {
    List<TagDTO> list =
        list().stream()
            .map(
                tag -> {
                  TagDTO tagDTO = new TagDTO();
                  BeanUtils.copyProperties(tag, tagDTO);
                  return tagDTO;
                })
            .toList();
    List<TagDTO> res = new ArrayList<>();

    for (TagDTO tagDTO : list) {
      Long count = blogService.countByTag(tagDTO.getId());
      if (count > 0) {
        tagDTO.setCount(count);
        res.add(tagDTO);
      }
    }
    return ApiResponse.success(res);
  }

  private Tag getTagByName(String name) {
    return lambdaQuery().eq(Tag::getName, name).one();
  }
}
